/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cw.controller;

import cw.controller.exceptions.IllegalOrphanException;
import cw.controller.exceptions.NonexistentEntityException;
import cw.controller.exceptions.PreexistingEntityException;
import cw.entity.Puzzle;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import cw.entity.Subject;
import cw.entity.PuzzleSize;
import cw.entity.Level;
import cw.entity.Question;
import java.util.ArrayList;
import java.util.List;
import cw.entity.GameResult;
import java.util.logging.Logger;

/**
 *
 * @author VinhTC
 */
public class PuzzleJpaController {

    public PuzzleJpaController() {
        emf = Persistence.createEntityManagerFactory("CrossWordsAdminPU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Puzzle puzzle) throws PreexistingEntityException, Exception {
        if (puzzle.getQuestionList() == null) {
            puzzle.setQuestionList(new ArrayList<Question>());
        }
        if (puzzle.getGameResultList() == null) {
            puzzle.setGameResultList(new ArrayList<GameResult>());
        }
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Subject subject = puzzle.getSubject();
            if (subject != null) {
                subject = em.getReference(subject.getClass(), subject.getSubjectId());
                puzzle.setSubject(subject);
            }
            PuzzleSize puzzleSize = puzzle.getPuzzleSize();
            if (puzzleSize != null) {
                puzzleSize = em.getReference(puzzleSize.getClass(), puzzleSize.getSizeId());
                puzzle.setPuzzleSize(puzzleSize);
            }
            Level level = puzzle.getLevel();
            if (level != null) {
                level = em.getReference(level.getClass(), level.getLevelId());
                puzzle.setLevel(level);
            }
            List<Question> attachedQuestionList = new ArrayList<Question>();
            for (Question questionListQuestionToAttach : puzzle.getQuestionList()) {
                questionListQuestionToAttach = em.getReference(questionListQuestionToAttach.getClass(), questionListQuestionToAttach.getQuestionId());
                attachedQuestionList.add(questionListQuestionToAttach);
            }
            puzzle.setQuestionList(attachedQuestionList);
            List<GameResult> attachedGameResultList = new ArrayList<GameResult>();
            for (GameResult gameResultListGameResultToAttach : puzzle.getGameResultList()) {
                gameResultListGameResultToAttach = em.getReference(gameResultListGameResultToAttach.getClass(), gameResultListGameResultToAttach.getUserId());
                attachedGameResultList.add(gameResultListGameResultToAttach);
            }
            puzzle.setGameResultList(attachedGameResultList);
            em.persist(puzzle);
            if (subject != null) {
                subject.getPuzzleList().add(puzzle);
                subject = em.merge(subject);
            }
            if (puzzleSize != null) {
                puzzleSize.getPuzzleList().add(puzzle);
                puzzleSize = em.merge(puzzleSize);
            }
            if (level != null) {
                level.getPuzzleList().add(puzzle);
                level = em.merge(level);
            }
            for (Question questionListQuestion : puzzle.getQuestionList()) {
                Puzzle oldPuzzleOfQuestionListQuestion = questionListQuestion.getPuzzle();
                questionListQuestion.setPuzzle(puzzle);
                questionListQuestion = em.merge(questionListQuestion);
                if (oldPuzzleOfQuestionListQuestion != null) {
                    oldPuzzleOfQuestionListQuestion.getQuestionList().remove(questionListQuestion);
                    oldPuzzleOfQuestionListQuestion = em.merge(oldPuzzleOfQuestionListQuestion);
                }
            }
            for (GameResult gameResultListGameResult : puzzle.getGameResultList()) {
                Puzzle oldPuzzleOfGameResultListGameResult = gameResultListGameResult.getPuzzle();
                gameResultListGameResult.setPuzzle(puzzle);
                gameResultListGameResult = em.merge(gameResultListGameResult);
                if (oldPuzzleOfGameResultListGameResult != null) {
                    oldPuzzleOfGameResultListGameResult.getGameResultList().remove(gameResultListGameResult);
                    oldPuzzleOfGameResultListGameResult = em.merge(oldPuzzleOfGameResultListGameResult);
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex) {
            if (findPuzzle(puzzle.getPuzzleId()) != null) {
                throw new PreexistingEntityException("Puzzle " + puzzle + " already exists.", ex);
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Puzzle puzzle) throws IllegalOrphanException, NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Puzzle persistentPuzzle = em.find(Puzzle.class, puzzle.getPuzzleId());
            Subject subjectOld = persistentPuzzle.getSubject();
            Subject subjectNew = puzzle.getSubject();
            PuzzleSize puzzleSizeOld = persistentPuzzle.getPuzzleSize();
            PuzzleSize puzzleSizeNew = puzzle.getPuzzleSize();
            Level levelOld = persistentPuzzle.getLevel();
            Level levelNew = puzzle.getLevel();
            List<Question> questionListOld = persistentPuzzle.getQuestionList();
            List<Question> questionListNew = puzzle.getQuestionList();
            List<GameResult> gameResultListOld = persistentPuzzle.getGameResultList();
            List<GameResult> gameResultListNew = puzzle.getGameResultList();
            List<String> illegalOrphanMessages = null;
            for (Question questionListOldQuestion : questionListOld) {
                if (!questionListNew.contains(questionListOldQuestion)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Question " + questionListOldQuestion + " since its puzzle field is not nullable.");
                }
            }
            for (GameResult gameResultListOldGameResult : gameResultListOld) {
                if (!gameResultListNew.contains(gameResultListOldGameResult)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain GameResult " + gameResultListOldGameResult + " since its puzzle field is not nullable.");
                }
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            if (subjectNew != null) {
                subjectNew = em.getReference(subjectNew.getClass(), subjectNew.getSubjectId());
                puzzle.setSubject(subjectNew);
            }
            if (puzzleSizeNew != null) {
                puzzleSizeNew = em.getReference(puzzleSizeNew.getClass(), puzzleSizeNew.getSizeId());
                puzzle.setPuzzleSize(puzzleSizeNew);
            }
            if (levelNew != null) {
                levelNew = em.getReference(levelNew.getClass(), levelNew.getLevelId());
                puzzle.setLevel(levelNew);
            }
            List<Question> attachedQuestionListNew = new ArrayList<Question>();
            for (Question questionListNewQuestionToAttach : questionListNew) {
                questionListNewQuestionToAttach = em.getReference(questionListNewQuestionToAttach.getClass(), questionListNewQuestionToAttach.getQuestionId());
                attachedQuestionListNew.add(questionListNewQuestionToAttach);
            }
            questionListNew = attachedQuestionListNew;
            puzzle.setQuestionList(questionListNew);
            List<GameResult> attachedGameResultListNew = new ArrayList<GameResult>();
            for (GameResult gameResultListNewGameResultToAttach : gameResultListNew) {
                gameResultListNewGameResultToAttach = em.getReference(gameResultListNewGameResultToAttach.getClass(), gameResultListNewGameResultToAttach.getUserId());
                attachedGameResultListNew.add(gameResultListNewGameResultToAttach);
            }
            gameResultListNew = attachedGameResultListNew;
            puzzle.setGameResultList(gameResultListNew);
            puzzle = em.merge(puzzle);
            if (subjectOld != null && !subjectOld.equals(subjectNew)) {
                subjectOld.getPuzzleList().remove(puzzle);
                subjectOld = em.merge(subjectOld);
            }
            if (subjectNew != null && !subjectNew.equals(subjectOld)) {
                subjectNew.getPuzzleList().add(puzzle);
                subjectNew = em.merge(subjectNew);
            }
            if (puzzleSizeOld != null && !puzzleSizeOld.equals(puzzleSizeNew)) {
                puzzleSizeOld.getPuzzleList().remove(puzzle);
                puzzleSizeOld = em.merge(puzzleSizeOld);
            }
            if (puzzleSizeNew != null && !puzzleSizeNew.equals(puzzleSizeOld)) {
                puzzleSizeNew.getPuzzleList().add(puzzle);
                puzzleSizeNew = em.merge(puzzleSizeNew);
            }
            if (levelOld != null && !levelOld.equals(levelNew)) {
                levelOld.getPuzzleList().remove(puzzle);
                levelOld = em.merge(levelOld);
            }
            if (levelNew != null && !levelNew.equals(levelOld)) {
                levelNew.getPuzzleList().add(puzzle);
                levelNew = em.merge(levelNew);
            }
            for (Question questionListNewQuestion : questionListNew) {
                if (!questionListOld.contains(questionListNewQuestion)) {
                    Puzzle oldPuzzleOfQuestionListNewQuestion = questionListNewQuestion.getPuzzle();
                    questionListNewQuestion.setPuzzle(puzzle);
                    questionListNewQuestion = em.merge(questionListNewQuestion);
                    if (oldPuzzleOfQuestionListNewQuestion != null && !oldPuzzleOfQuestionListNewQuestion.equals(puzzle)) {
                        oldPuzzleOfQuestionListNewQuestion.getQuestionList().remove(questionListNewQuestion);
                        oldPuzzleOfQuestionListNewQuestion = em.merge(oldPuzzleOfQuestionListNewQuestion);
                    }
                }
            }
            for (GameResult gameResultListNewGameResult : gameResultListNew) {
                if (!gameResultListOld.contains(gameResultListNewGameResult)) {
                    Puzzle oldPuzzleOfGameResultListNewGameResult = gameResultListNewGameResult.getPuzzle();
                    gameResultListNewGameResult.setPuzzle(puzzle);
                    gameResultListNewGameResult = em.merge(gameResultListNewGameResult);
                    if (oldPuzzleOfGameResultListNewGameResult != null && !oldPuzzleOfGameResultListNewGameResult.equals(puzzle)) {
                        oldPuzzleOfGameResultListNewGameResult.getGameResultList().remove(gameResultListNewGameResult);
                        oldPuzzleOfGameResultListNewGameResult = em.merge(oldPuzzleOfGameResultListNewGameResult);
                    }
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = puzzle.getPuzzleId();
                if (findPuzzle(id) == null) {
                    throw new NonexistentEntityException("The puzzle with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws IllegalOrphanException, NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Puzzle puzzle;
            try {
                puzzle = em.getReference(Puzzle.class, id);
                puzzle.getPuzzleId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The puzzle with id " + id + " no longer exists.", enfe);
            }
            List<String> illegalOrphanMessages = null;
            List<Question> questionListOrphanCheck = puzzle.getQuestionList();
            for (Question questionListOrphanCheckQuestion : questionListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Puzzle (" + puzzle + ") cannot be destroyed since the Question " + questionListOrphanCheckQuestion + " in its questionList field has a non-nullable puzzle field.");
            }
            List<GameResult> gameResultListOrphanCheck = puzzle.getGameResultList();
            for (GameResult gameResultListOrphanCheckGameResult : gameResultListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Puzzle (" + puzzle + ") cannot be destroyed since the GameResult " + gameResultListOrphanCheckGameResult + " in its gameResultList field has a non-nullable puzzle field.");
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            Subject subject = puzzle.getSubject();
            if (subject != null) {
                subject.getPuzzleList().remove(puzzle);
                subject = em.merge(subject);
            }
            PuzzleSize puzzleSize = puzzle.getPuzzleSize();
            if (puzzleSize != null) {
                puzzleSize.getPuzzleList().remove(puzzle);
                puzzleSize = em.merge(puzzleSize);
            }
            Level level = puzzle.getLevel();
            if (level != null) {
                level.getPuzzleList().remove(puzzle);
                level = em.merge(level);
            }
            em.remove(puzzle);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Puzzle> findPuzzleEntities() {
        return findPuzzleEntities(true, -1, -1);
    }

    public List<Puzzle> findPuzzleEntities(int maxResults, int firstResult) {
        return findPuzzleEntities(false, maxResults, firstResult);
    }

    private List<Puzzle> findPuzzleEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(Puzzle.class));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Puzzle findPuzzle(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Puzzle.class, id);
        } finally {
            em.close();
        }
    }

    public int getPuzzleCount() {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Puzzle> rt = cq.from(Puzzle.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

    /**
     * getPuzzleByName
     * @param puzzleName
     * @return Puzzle
     */
    public Puzzle getPuzzleByName(String puzzleName) {
        Puzzle puzzle = null;
        try {
            Query query = getEntityManager().createNamedQuery("Puzzle.findByPuzzleName");
            query.setParameter("puzzleName", puzzleName);

            puzzle = (Puzzle) query.getSingleResult();
        } catch (Exception e) {
            Logger.getLogger(PuzzleJpaController.class.getName()).log(java.util.logging.Level.SEVERE, null, e);
        }
        return puzzle;
    }

    /**
     * getPuzzleByLevelId
     * @param levelId
     * @return List<Puzzle>
     */
    public List<Puzzle> getPuzzleByLevelId(int levelId, int sizeId, int subjectId) {
        List<Puzzle> lst = new ArrayList<Puzzle>();
        try {
            Query query = getEntityManager().createNamedQuery("Puzzle.findByLevelSizeSubject");
            query.setParameter("levelId", levelId);
            query.setParameter("sizeId", sizeId);
            query.setParameter("subjectId", subjectId);

            lst = query.getResultList();
//            List<Puzzle> temp = null;
//            temp = findPuzzleEntities();
//            System.out.println("size of puzzle"+ temp.size());
//            for (int i= 0; i < temp.size(); i++) {
//                Puzzle puzzle = temp.get(i);
//                if (puzzle.getLevel().getLevelId()==levelId && puzzle.getPuzzleSize().getSizeId() == sizeId && puzzle.getSubject().getSubjectId() == subjectId) {
//                    lst.add(puzzle);
//                }
//            }
        } catch (Exception e) {
            Logger.getLogger(PuzzleJpaController.class.getName()).log(java.util.logging.Level.SEVERE, null, e);
        }
        return lst;
    }

    /**
     * getPuzzleBySubjectId
     * @param subjectId
     * @return List<Puzzle>
     */
    public List<Puzzle> getPuzzleBySubjectId(int subjectId) {
        try {
            Query query = getEntityManager().createNamedQuery("Puzzle.findBySubjectId");
            query.setParameter("subjectId", subjectId);

            return query.getResultList();
        } catch (Exception e) {
            Logger.getLogger(PuzzleJpaController.class.getName()).log(java.util.logging.Level.SEVERE, null, e);
            return null;
        }
    }
}
